
from decimal import Decimal
from typing import List, Optional

import pandas as pd

from src.core.backtest.context import Context
from src.core.domain.position import Position
from src.core.indicators.indicator import RequiresIndicators, Indicator
from src.core.indicators.standard_indicators import ATRIndicator
from src.core.instruction.instruction import Instruction
from src.core.strategy.stop_loss.stop_loss_manager import StopLossManager
from src.enums.enums import Operation, Direction
from src.enums.timeframe import Timeframe

ZERO = Decimal('0')


class ATRTrailingStop(StopLossManager, RequiresIndicators):
    """
    ATR 移动止损管理器。

    该止损策略在每个K线周期动态更新止损价格：
    - 对于多头持仓: 止损位 = max(上一止损位, 当前最高价 - ATR * multiplier)。
                   止损位只会向上移动或保持不变。
    - 对于空头持仓: 止损位 = min(上一止损位, 当前最低价 + ATR * multiplier)。
                   止损位只会向下移动或保持不变。

    这允许策略在保护初始风险的同时，能够锁定浮动盈利。
    每个仓位(Position)都会拥有自己独立的ATRTrailingStop实例。
    """

    def __init__(self, timeframe: Timeframe, atr_period: int, atr_multiple: float):
        """
        通过依赖注入进行配置。

        Args:
            timeframe (Timeframe): 要计算ATR并执行止损的时间周期。
            atr_period (int): ATR的计算周期。
            atr_multiple (float): ATR的倍数。
        """
        super().__init__()
        self.timeframe_str = timeframe.value
        self.atr_period = atr_period
        self.atr_multiple = Decimal(str(atr_multiple))

        # --- 状态 (每个实例独有) ---
        # 当前有效的追踪止损价格。
        self.trailing_stop_price: Decimal = ZERO

        self.atr_request = ATRIndicator(
            period=atr_period,
            timeframe=timeframe.value
        )

    def get_required_indicators(self) -> List[Indicator]:
        """声明需要的ATR指标实例。"""
        return [self.atr_request]

    def check_exit(self, position: Position, current_bar: pd.Series) -> Optional[Instruction]:
        """
        【在Bar内检查触发】
        由引擎在每个Bar的开始阶段调用，用于判断是否触发止损。
        这个方法不应修改自身状态。

        Args:
            position (Position): 正在被评估的仓位。
            current_bar (pd.Series): 当前K线的完整数据 (OHLC)。

        Returns:
            Optional[Decimal]: 如果触发止损，返回预期的成交价格；否则返回None。
        """
        if self.trailing_stop_price == ZERO:
            # 状态尚未初始化，不可能触发
            return None

        price_low = Decimal(str(current_bar[(self.timeframe_str, 'low')]))
        price_high = Decimal(str(current_bar[(self.timeframe_str, 'high')]))

        # 检查多头止损 (价格路径假设 O -> L -> H -> C)
        if position.is_long():
            if price_low <= self.trailing_stop_price:
                # 触发！成交价应为设定的止损价，但不能优于市场价(例如跳空)
                return Instruction(Operation.SELL,
                                   Direction.LONG,
                                   self.trailing_stop_price,
                                   position.quantity,
                                   'ATR trailing stop'
                                   )

        # 检查空头止损 (价格路径假设 O -> H -> L -> C)
        else:  # is_short
            if price_high >= self.trailing_stop_price:
                # 触发！
                return Instruction(Operation.BUY,
                                   Direction.SHORT,
                                   self.trailing_stop_price,
                                   position.quantity,
                                   'ATR trailing stop'
                                   )

        return None  # 未触发

    def update_state(self, position: Position, context: Context):
        """
        【在Bar结束后更新状态】
        由引擎在每个Bar的结束阶段调用，用于为下一个Bar更新追踪止损价格。

        Args:
            position (Position): 正在被评估的仓位。
            context (Context): 提供对最新已收盘K线数据的访问。
        """
        latest_bar = context.get_latest_bar()
        if latest_bar is None:
            return

        current_atr = Decimal(str(latest_bar[self.atr_request.column_name]))
        price_high = Decimal(str(latest_bar[(self.timeframe_str, 'high')]))
        price_low = Decimal(str(latest_bar[(self.timeframe_str, 'low')]))

        # --- 首次初始化止损价 ---
        # 如果trailing_stop_price为0，说明这是开仓后的第一根完整K线
        if self.trailing_stop_price == ZERO:
            if position.is_long():
                self.trailing_stop_price = position.entry_price - (current_atr * self.atr_multiple)
            else:  # is_short
                self.trailing_stop_price = position.entry_price + (current_atr * self.atr_multiple)
            print(
                f"INFO ({context.current_time}): "
                f"Position {position.id} initial stop loss set to {self.trailing_stop_price:.4f}")

        # --- 更新追踪止损价 ---
        if position.is_long():
            # 计算潜在的新止损位
            potential_new_stop = price_high - (current_atr * self.atr_multiple)
            # 止损位只上不下
            if potential_new_stop > self.trailing_stop_price:
                self.trailing_stop_price = potential_new_stop
        else:  # is_short
            # 计算潜在的新止损位
            potential_new_stop = price_low + (current_atr * self.atr_multiple)
            # 止损位只下不上
            if potential_new_stop < self.trailing_stop_price:
                self.trailing_stop_price = potential_new_stop
